package com.vogue.flowmeter.profile.callback;

import android.bluetooth.BluetoothDevice;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import com.vogue.flowmeter.api.event.MessageEvent;
import com.vogue.flowmeter.util.ConvertUtils;
import com.vogue.flowmeter.util.DateUtils;
import org.greenrobot.eventbus.EventBus;
import java.util.Calendar;
import no.nordicsemi.android.ble.callback.DataSentCallback;
import no.nordicsemi.android.ble.callback.profile.ProfileDataCallback;
import no.nordicsemi.android.ble.common.profile.DateTimeCallback;
import no.nordicsemi.android.ble.data.Data;

/**
 * 读写回调（数据处理）
 */
@SuppressWarnings("ConstantConditions")
public abstract class VogueBleReadAndWriteCallback implements ProfileDataCallback, DataSentCallback, DateTimeCallback, VogueBleDataCallback {
    /**
     * 在收到日期时间数据包时调用回调。
     * @param device
     * @param calendar
     */
    @Override
    public void onDateTimeReceived(@NonNull BluetoothDevice device, @NonNull Calendar calendar) {
        Log.e("DataCallback", "Read--onDateTimeReceived: " + DateUtils.format(calendar.getTime().getTime(),DateUtils.DEFAULT_DATE_FORMAT));
    }

    /**
     * 读数据回调
     * 每次读取值或使用通知或指示更改值时都收到回调。
     *
     * @param device
     * @param data
     */
    @Override
    public void onDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data) {
        Log.e("DataCallback", "Read--onDataReceived: " + new String(data.getValue()));
        received(device, data);
    }

    /**
     * 写数据回调
     * 每次将值写入特征或描述符，或从服务器特征或描述符读取时接收的回调。
     *
     * @param device
     * @param data
     */
    @Override
    public void onDataSent(@NonNull final BluetoothDevice device, @NonNull final Data data) {
        Log.e("DataCallback", "Write--onDataSent: " + new String(data.getValue()));
        sent(device, data);
    }

    @Override
    public void onInvalidDataReceived(@NonNull final BluetoothDevice device, @NonNull final Data data) {
        // 只有我们读了数据才能使其无效。我们假设应用程序总是发送正确的数据。
        Log.e("DataCallback", "Read--onInvalidDataReceived: " + new String(data.getValue()));
        MessageEvent event = new MessageEvent("Invalid", "Invalid", "Invalid data received: " + new String(data.getValue()));
        EventBus.getDefault().post(event);
    }

    /**
     * 读 解析数据
     *
     * @param device 目标设备
     * @param data   数据
     */
    private void received(@NonNull final BluetoothDevice device, @NonNull final Data data) {
        final String write = new String(data.getValue());
        if (write.length() < 18) {
            onInvalidDataReceived(device, data);
            return;
        }

        Builder builder=getData(write);
        String  opCode = builder.getControl().toLowerCase();

        String control = getString(opCode);

        //校验和 核对
        if (builder.checkSum()) {
            //数据校验通过
            onMessageEvent("tx",opCode, control, control.contains("控制字：") ? "未知返回，解析失败" : msg(builder.getData(),opCode), builder.getOriginal());
        } else {
            //数据校验不通过
            onMessageEvent("tx",opCode, control, "控制字：" + opCode+" 校验和 校验失败", builder.getOriginal());
        }


    }


    /**
     * 写 解析数据
     *
     * @param device 目标设备
     * @param data   数据
     */
    private void sent(@NonNull final BluetoothDevice device, @NonNull final Data data) {

        final String write = new String(data.getValue());
        if (write.length() < 18) {
            onInvalidDataReceived(device, data);
            return;
        }

        Builder builder=getData(write);

        String opCode = builder.getControl();

        String control = getString(opCode);

        //校验和 核对
        if (builder.checkSum()) {
            //数据校验通过
            onMessageEvent("rx",opCode, control, control.contains("控制字：") ? opCode+" 未知返回，解析失败" : msg(builder.getData(),opCode), builder.getOriginal());
        } else {
            //数据校验不通过
            onMessageEvent("rx",opCode, control, "控制字：" + opCode+" 校验和 校验失败", builder.getOriginal());
        }


    }

    /**
     * 获取操作名称
     * @param opCode
     * @return
     */
    private String getString(String opCode) {
        String control;
        switch (opCode) {
            //TODO 读取指令

            case "08":
                control = "读取软件版本号";
                break;
            case "10":
                control = "读设备PID";
                break;
            case "14":
                control = "读卡号";
                break;
            case "15":
                control = "读设备masterIP";
                break;
            case "16":
                control = "读设备subIP";
                break;
            case "19":
                control = "读历史水量记录";
                break;
            case "1a":
                control = "读冻结水量记录";
                break;
            case "20":
                control = "读取采样周期";
                break;
            case "21":
                control = "读取发送周期";
                break;
            case "22":
                control = "读取日历时钟";
                break;

            case "40":
                control = "读IMEI";
                break;
            case "41":
                control = "读IMSI";
                break;
            case "42":
                control = "读USIM ID";
                break;
            case "43":
                control = "读信号强度CSQ";
                break;
            case "51":
                control = "读外接水表水量";
                break;

            //TODO 写入指令

            case "0a":
                control = "系统重启";
                break;
            case "90":
                control = "写设备PID";
                break;
            case "94":
                control = "设置卡编号";
                break;
            case "95":
                control = "写设备masterIP";
                break;
            case "96":
                control = "写设备subIP";
                break;
            case "a0":
                control = "写采样周期";
                break;
            case "a1":
                control = "写发送周期";
                break;
            case "a2":
                control = "写日历时钟";
                break;
            case "c0":
                control = "远端强制常规发送";
                break;

            case "e0":
                control = "主设备升级";
                //TODO  升级解析

                break;
            case "e1":
                control = "从设备升级";
                //TODO  升级解析

                break;
            case "ea":
                control = "外部参数设置";
                //TODO  外部参数解析

                break;
            case "f4":
                control="200字节测试";
                break;

            default:
                control = "控制字：" + opCode;
        }
        return control;
    }

    /**
     * 数据区数据处理
     * @param msg
     * @param opCode 需处理的控制字
     * @return
     */
    private String msg(String msg,String opCode){
        if (TextUtils.isEmpty(msg)){
            return "";
        }

        switch (opCode) {
            //TODO 读取指令

            case "08":
                //TODO  "读取软件版本号";
                return ConvertUtils.hexStr2Str(msg);
            case "10":
                //TODO  "读设备PID";
                return ConvertUtils.hexStr2Str(msg);
            case "14":
                //TODO  "读卡号";
                return ConvertUtils.hexStr2Str(msg);
            case "15":
                //TODO  "读设备masterIP";
                return ConvertUtils.hexStr2Str(msg).replace(",",":");
            case "16":
                //TODO  "读设备subIP";
                return ConvertUtils.hexStr2Str(msg).replace(",",":");
            case "19":
                //TODO  "读历史水量记录";
                return ConvertUtils.hexStr2Str(msg);
            case "1a":
                //TODO  "读冻结水量记录";
                return ConvertUtils.hexStr2Str(msg);
            case "20":
                //TODO  "读取采样周期";
                return Integer.valueOf(msg)+"";
            case "21":
                //TODO  "读取发送周期";
                return Integer.valueOf(msg)+"";
            case "22":
                //TODO  "读取日历时钟";
                return msg;
            case "40":
                //TODO  "读IMEI";
                return ConvertUtils.hexStr2Str(msg);
            case "41":
                //TODO  "读IMSI";
                return ConvertUtils.hexStr2Str(msg);
            case "42":
                //TODO  "读USIM ID";
                return ConvertUtils.hexStr2Str(msg);
            case "43":
                //TODO  "读信号强度CSQ";
                return msg;
            case "51":
                //TODO  "读外接水表水量";
                return ConvertUtils.hexStr2Str(msg);

            //TODO 写入指令

            case "0a":
                //TODO  "系统重启";
                return ConvertUtils.hexStr2Str(msg);
            case "90":
                //TODO  "写设备PID";
                return ConvertUtils.hexStr2Str(msg);
            case "94":
                //TODO  "设置卡编号";
                return ConvertUtils.hexStr2Str(msg);
            case "95":
                //TODO  "写设备masterIP";
                return ConvertUtils.hexStr2Str(msg).replace(",",":");
            case "96":
                //TODO  "写设备subIP";
                return ConvertUtils.hexStr2Str(msg).replace(",",":");
            case "a0":
                //TODO  "写采样周期";
                return Integer.valueOf(msg)+"";
            case "a1":
                //TODO  "写发送周期";
                return Integer.valueOf(msg)+"";
            case "a2":
                //TODO  "写日历时钟";
                return msg;
            case "c0":
                //TODO  "远端强制常规发送";
                return ConvertUtils.hexStr2Str(msg);
            case "e0":
                //TODO  "主设备升级";
                return ConvertUtils.hexStr2Str(msg);
            case "e1":
                //TODO  "从设备升级";
                return ConvertUtils.hexStr2Str(msg);
            case "ea":
                //TODO  "外部参数设置";

                return external(msg);
            case "f4":
                return ConvertUtils.hexStr2Str(msg);
            default:
                //默认不处理
                return msg;
        }
    }

    /**
     * 外部设备参数设置
     * @param msg
     * @return
     */
    private String external(String msg){
        msg=ConvertUtils.hexStr2Str(msg);
        Log.e("external=",msg);

        String address=msg.substring(0,4);

        switch (address) {
            case "0060":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));
            case "0061":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));

            case "0071":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));
            case "0072":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));
            case "0073":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));
            case "0074":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));
            case "0075":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));

            case "0033":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));
            case "0035":

                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));
            case "0036":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));
            case "0037":
                return  ConvertUtils.hexToLong(msg.substring(4, msg.length()));

            default:
                return  ConvertUtils.hexToFloat(msg.substring(4, msg.length()));
        }
    }

    /**
     * 解析返回数据
     * @param data
     * @return
     */
    private Builder getData(String data){

        int length=Integer.parseInt(data.substring(14,18),16)*2;


        return new Builder(data)
                .setHeader(data.substring(0,2))
                .setIdentification(data.substring(2,6))
                .setDirection(data.substring(6,8))
                .setAddress(data.substring(8,10))
                .setEncryption(data.substring(10,12))
                .setControl(data.substring(12,14))
                .setDataLength(length+"")
                .setData(data.substring(18,18+length))
                .setChecksum(data.substring(18+length,18+length+2))
                .setEnd(data.substring(18+length+2,18+length+4));
    }
    /**
     * 协议构造器
     */
    public static class Builder {

        //帧头
        private String header;
        //设备标识
        private String identification;
        //传输方向
        private String direction;
        //地址
        private String address;
        //加密版本
        private String encryption;
        //控制字
        private String control;
        //数据长度
        private String dataLength;
        //数据
        private String data;
        //校验和
        private String checksum;
        //帧尾
        private String end;

        private String original;

        public Builder(String original) {
            this.original=original;
        }

        public Builder setHeader(String header) {
            this.header = header;
            return this;
        }

        public Builder setIdentification(String identification) {
            this.identification = identification;
            return this;
        }

        public Builder setDirection(String direction) {
            this.direction = direction;
            return this;
        }

        public Builder setAddress(String address) {
            this.address = address;
            return this;
        }

        public Builder setEncryption(String encryption) {
            this.encryption = encryption;
            return this;
        }

        public Builder setControl(String control) {
            this.control = control;
            return this;
        }

        public Builder setDataLength(String dataLength) {
            this.dataLength = dataLength;
            return this;
        }

        public Builder setData(String data) {
            this.data = data;
            return this;
        }

        public Builder setChecksum(String checksum) {
            this.checksum = checksum;
            return this;
        }

        public Builder setEnd(String end) {
            this.end = end;
            return this;
        }


        public String getHeader() {
            return header;
        }

        public String getIdentification() {
            return identification;
        }

        public String getDirection() {
            return direction;
        }

        public String getAddress() {
            return address;
        }

        public String getEncryption() {
            return encryption;
        }

        public String getControl() {
            return control;
        }

        public String getDataLength() {
            return dataLength;
        }

        public String getData() {
            return data;
        }

        public String getChecksum() {
            return checksum;
        }

        public String getEnd() {
            return end;
        }

        public String getOriginal() {
            return original;
        }

        public boolean checkSum() {
            String check= header+identification+direction+address+encryption+control+String.format("%04x",(Integer.parseInt(dataLength)/2))+data;

            Log.e("checkSum","data: "+check +"  sum: "+ConvertUtils.makeCheckSum(check)+" sum: "+getChecksum());

           return getChecksum().equals(ConvertUtils.makeCheckSum(check));
        }
        @Override
        public String toString() {
            return "{" +
                    "帧头='" + header + '\'' +
                    ", 设备标识='" + identification + '\'' +
                    ", 传输方向='" + direction + '\'' +
                    ", 地址='" + address + '\'' +
                    ", 加密版本='" + encryption + '\'' +
                    ", 控制字='" + control + '\'' +
                    ", 数据长度='" + dataLength + '\'' +
                    ", 数据='" + data + '\'' +
                    ", 校验和='" + checksum + '\'' +
                    ", 帧尾='" + end + '\'' +
                    '}'+" {" +"原始数据='" + original + '\''+'}';
        }
    }
}
